package org.liu.async.interceptors;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.liu.async.Constant;
import org.liu.async.kit.AsyncConfig;
import org.liu.async.kit.AsyncReturnObject;
import org.liu.async.kit.ObjectFuture;
import org.liu.async.transom.TemporaryData;

import java.lang.reflect.Method;
import java.util.concurrent.*;

/**
 * Created by liuj on 2015/12/13.
 * 异步执行的主要拦截器,核心类
 */
public class JobInterceptor implements MethodInterceptor {

    /**
     * 异步执行的线程池
     */
    private final ThreadPoolExecutor threadPoolExecutor;

    /**
     * 异步执行的配置
     */
    private AsyncConfig asyncConfig;

    public JobInterceptor(AsyncConfig asyncConfig) {
        this.asyncConfig = asyncConfig;
        if(asyncConfig.isAsync()) {
            threadPoolExecutor = new ThreadPoolExecutor(asyncConfig.getCoreSize(), asyncConfig.getCoreSize(), 60,
                    TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(asyncConfig.getQueueSize())/*, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread= new Thread(r);
                thread.setName("JobInterceptor ThreadFactory");
                thread.setContextClassLoader(this.getClass().getClassLoader());
                return thread;
            }
        }*/, new ThreadPoolExecutor.CallerRunsPolicy());
        }else{
            threadPoolExecutor=null;
        }
    }

    @Override
    public Object intercept(final Object o, final Method method, final Object[] objects, final MethodProxy methodProxy) throws Throwable {
        if (!Constant.async || !asyncConfig.isAsync()) {
            return methodProxy.invokeSuper(o, objects);
        }
        Future<Object> future = threadPoolExecutor.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                try {
                    Object object = methodProxy.invokeSuper(o, objects);
                    return object;
                } catch (Throwable throwable) {
//                    throwable.printStackTrace();
//                    throw new AsyncTimeoutException("timeouted");
                    return new TemporaryData(new RuntimeException(throwable), null);
                }

            }
        });
        ObjectFuture objectFuture1 = new ObjectFuture(future, asyncConfig.getTimeout(), asyncConfig.getTimeUnit());
//        objectFuture.get();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(method.getReturnType());
//        enhancer.setInterfaces(new Class[]{});
        enhancer.setCallback(new AsyncReturnObject(objectFuture1));
//        enhancer.setCallback(NoOp.INSTANCE);
        return enhancer.create();
    }

    public void destroy() {
        threadPoolExecutor.shutdown();
    }

    public AsyncConfig getAsyncConfig() {
        return asyncConfig;
    }
}
